package com.yuanda.erp9.syn.service.erp9.impl;

import com.innovation.ic.b1b.framework.util.StringUtils;
import com.yuanda.erp9.syn.entity.CmsTopicUpdateLogEntity;
import com.yuanda.erp9.syn.enums.CachePrefixEnum;
import com.yuanda.erp9.syn.enums.SourceEnum;
import com.yuanda.erp9.syn.enums.SubjectSupplierEnum;
import com.yuanda.erp9.syn.event.EmaiImapSaveFileEvent;
import com.yuanda.erp9.syn.exception.TargetServerException;
import com.yuanda.erp9.syn.service.erp9.CmsTopicUpdateLogService;
import com.yuanda.erp9.syn.service.erp9.ImapReceiveMailService;
import com.yuanda.erp9.syn.util.CacheUtil;
import com.yuanda.erp9.syn.util.DateUtils;
import com.yuanda.erp9.syn.value.ImapMailProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Service
@Slf4j
public class ImapReceiveMailServiceImpl implements ImapReceiveMailService {


    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private ImapMailProperty imapMailProperty;

    @Autowired
    private CmsTopicUpdateLogService cmsTopicUpdateLogService;

    //ssl加密
    final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";

    /*定义连接imap服务器的属性信息*/
    private final String protocol = "imap";

    /*供应商id不能为空*/
    private final Integer exceptionCode = -10;

    private final String exceptionMsg = "保存邮件附件发生异常";


    static {
        System.setProperty("mail.mime.base64.ignoreerrors", "true");
    }


    @Override
    public void downloadImapMail() {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        //有些参数可能不需要
        Properties props = new Properties();
        props.setProperty("mail.imap.socketFactory.class", SSL_FACTORY);
        props.setProperty("mail.imap.socketFactory.fallback", "false");
        props.setProperty("mail.transport.protocol", protocol);
        props.setProperty("mail.imap.port", imapMailProperty.getPort() + "");
        props.setProperty("mail.imap.socketFactory.port", imapMailProperty.getPort() + "");
        Folder folder = null;
        Store store = null;
        try {
            //创建会话
            Session session = Session.getInstance(props);
            //存储对象
            store = session.getStore("imap");
            //连接
            store.connect(imapMailProperty.getServer(), imapMailProperty.getPort(), imapMailProperty.getUserName(), imapMailProperty.getToken());
            // 获得收件箱
            folder = store.getFolder("INBOX");
            // 以读写模式打开收件箱
            folder.open(Folder.READ_WRITE);
            Message[] messages = folder.getMessages();
            parseMessage(imapMailProperty.getSavePath(), true, messages);
        } catch (Exception e) {
            log.error("解析mail异常 邮箱 {} ,登陆账号 {} ", imapMailProperty.getServer(), imapMailProperty.getServer(), e);
            throw new TargetServerException(e.getMessage(), exceptionCode, exceptionMsg);
        } finally {
            if (folder != null) {
                try {
                    folder.close(true);
                } catch (MessagingException e) {
                    log.error("folder关闭流异常", e);
                }
            }
            if (store != null) {
                try {
                    store.close();
                } catch (MessagingException e) {
                    log.error("store关闭流异常", e);
                }
            }
        }

    }


    /**
     * 解析邮件
     *
     * @param messages 要解析的邮件列表
     */
    private void parseMessage(String savePath, boolean isDelete, Message... messages){
        if (messages == null || messages.length < 1) {
            log.info("未找到要解析的邮件");
            return;
        }
        // 解析所有邮件
        for (int i = 0, count = messages.length; i < count; i++) {
            try {
                MimeMessage msg = (MimeMessage) messages[i];
                String subject = getSubject(msg);
                Date sentDate = msg.getSentDate();
                if (!DateUtils.isSameDay(sentDate.getTime())) {
                    continue;
                }
                log.info("------------------解析第 {} 封邮件-------------------- ", msg.getMessageNumber());
                log.info("主题: " + subject);
                log.info("发件人: " + getFrom(msg));
                log.info("发送时间：" + getSentDate(msg, null));
                boolean isContainerAttachment = isContainAttachment(msg);
                log.info("是否包含附件：" + isContainerAttachment);

                String supplier = SubjectSupplierEnum.getSupplier(subject);
                if(!StringUtils.validateParameter(supplier)){
                    supplier = SubjectSupplierEnum.getLikeSupplier(subject);
                }
                if(!StringUtils.validateParameter(supplier)){
                    log.warn("supplier 未找到对应的供应商枚举类型 subject : {} ",subject);
                    continue;
                }
                if (isContainerAttachment) {
                    //保存附件路径
                    saveAttachment(msg, savePath, subject); //保存附件路径
                }
                StringBuffer content = new StringBuffer(30);
                getMailTextContent(msg, content);
                System.out.println("邮件正文：" + (content.length() > 100 ? content.substring(0, 100) + "..." : content));
                log.info("------------------解析第 {} 封邮件结束-------------------- ", msg.getMessageNumber());
                System.out.println();
                if(isDelete){
                    msg.setFlag(Flags.Flag.DELETED, isDelete);
                    log.info("------------------解析第 {} 封邮件删除-------------------- ", msg.getMessageNumber());
                }
            } catch (Exception e) {
                log.error("------------------解析第 {} 封邮件发生异常-------------------- ", i, e);
            }
        }
    }


    /**
     * 获得邮件主题
     *
     * @param msg 邮件内容
     * @return 解码后的邮件主题
     */
    private String getSubject(MimeMessage msg) throws UnsupportedEncodingException, MessagingException {
        return MimeUtility.decodeText(msg.getSubject());
    }

    /**
     * 获得邮件发件人
     *
     * @param msg 邮件内容
     * @return 姓名 <Email地址>
     * @throws MessagingException
     * @throws UnsupportedEncodingException
     */
    private String getFrom(MimeMessage msg) throws MessagingException, UnsupportedEncodingException {
        String from = "";
        Address[] froms = msg.getFrom();
        if (froms.length < 1) {
            throw new MessagingException("没有发件人!");
        }
        InternetAddress address = (InternetAddress) froms[0];
        String person = address.getPersonal();
        if (person != null) {
            person = MimeUtility.decodeText(person) + " ";
        } else {
            person = "";
        }
        from = person + "<" + address.getAddress() + ">";

        return from;
    }


    /**
     * 获得邮件发送时间
     *
     * @param msg 邮件内容
     * @return yyyy年mm月dd日 星期X HH:mm
     * @throws MessagingException
     */
    private String getSentDate(MimeMessage msg, String pattern) throws MessagingException {
        Date receivedDate = msg.getSentDate();
        if (receivedDate == null) {
            return "";
        }
        if (pattern == null || "".equals(pattern)) {
            pattern = "yyyy年MM月dd日 E HH:mm ";
        }
        return new SimpleDateFormat(pattern).format(receivedDate);
    }

    /**
     * 判断邮件中是否包含附件
     * <p>
     * //* @param msg 邮件内容
     *
     * @return 邮件中存在附件返回true，不存在返回false
     * @throws MessagingException
     * @throws IOException
     */
    private boolean isContainAttachment(Part part) throws MessagingException, IOException {
        boolean flag = false;
        if (part.isMimeType("multipart/*")) {
            MimeMultipart multipart = (MimeMultipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    flag = true;
                } else if (bodyPart.isMimeType("multipart/*")) {
                    flag = isContainAttachment(bodyPart);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.indexOf("application") != -1) {
                        flag = true;
                    }

                    if (contentType.indexOf("name") != -1) {
                        flag = true;
                    }
                }
                if (flag) {
                    break;
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            flag = isContainAttachment((Part) part.getContent());
        }
        return flag;
    }


    /**
     * 获得邮件文本内容
     *
     * @param part    邮件体
     * @param content 存储邮件文本内容的字符串
     * @throws MessagingException
     * @throws IOException
     */
    private void getMailTextContent(Part part, StringBuffer content) throws MessagingException, IOException {
        //如果是文本类型的附件，通过getContent方法可以取到文本内容，但这不是我们需要的结果，所以在这里要做判断
        boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
        if (part.isMimeType("text/*") && !isContainTextAttach) {
            content.append(part.getContent().toString());
        } else if (part.isMimeType("message/rfc822")) {
            getMailTextContent((Part) part.getContent(), content);
        } else if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                getMailTextContent(bodyPart, content);
            }
        }
    }

    /**
     * 保存附件
     *
     * @param part    邮件中多个组合体中的其中一个组合体
     * @param destDir 附件保存目录
     * @throws UnsupportedEncodingException
     * @throws MessagingException
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void saveAttachment(Part part, String destDir, String subject) throws UnsupportedEncodingException, MessagingException,
            FileNotFoundException, IOException {
        if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();    //复杂体邮件
            //复杂体邮件包含多个邮件体
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                //获得复杂体邮件中其中一个邮件体
                BodyPart bodyPart = multipart.getBodyPart(i);
                //某一个邮件体也有可能是由多个邮件体组成的复杂体
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    InputStream is = bodyPart.getInputStream();
                    saveFile(is, destDir, decodeText(bodyPart.getFileName()), subject);
                } else if (bodyPart.isMimeType("multipart/*")) {
                    saveAttachment(bodyPart, destDir, subject);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.indexOf("name") != -1 || contentType.indexOf("application") != -1) {
                        saveFile(bodyPart.getInputStream(), destDir, decodeText(bodyPart.getFileName()), subject);
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            saveAttachment((Part) part.getContent(), destDir, subject);
        }
    }


    /**
     * 读取输入流中的数据保存至指定目录
     *
     * @param is       输入流
     * @param fileName 文件名
     * @param destDir  文件存储目录
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void saveFile(InputStream is, String destDir, String fileName, String subject) throws IOException {
        File file = new File(destDir);
        if (!file.exists()) {
            file.mkdirs();
        }
        String filePath = destDir + fileName;
        try (
                FileOutputStream fos = new FileOutputStream(new File(filePath));
                BufferedInputStream bis = new BufferedInputStream(is);
                BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            int len = -1;
            while ((len = bis.read()) != -1) {
                bos.write(len);
                bos.flush();
            }
        } catch (Exception e) {
            log.error("保存邮件附件发生异常 : ", e);
            throw e;
        }
        Map<String, String> param = new HashMap<>();
        param.put("filePath", filePath);
        param.put("subject", subject);
        String supplier = SubjectSupplierEnum.getSupplier(subject);
        if(!StringUtils.validateParameter(supplier)){
            supplier = SubjectSupplierEnum.getLikeSupplier(subject);
        }
        if(!StringUtils.validateParameter(supplier)){
            log.warn("supplier 未找到对应的供应商枚举类型 subject : {} ",subject);
            return ;
        }
        Integer supplierId = CacheUtil.init(CachePrefixEnum.SUPPLIERS.getKey()).get(supplier);
        CmsTopicUpdateLogEntity cmsTopicUpdateLogEntity = new CmsTopicUpdateLogEntity();
        cmsTopicUpdateLogEntity.setSystemTopicType(1);
        cmsTopicUpdateLogEntity.setUpdated(1);
        cmsTopicUpdateLogEntity.setSystemTopic(subject);
        cmsTopicUpdateLogEntity.setCreateAt(new Date());
        cmsTopicUpdateLogEntity.setSystemTopicType(SourceEnum.EMAIL.getCode());
        cmsTopicUpdateLogEntity.setSupplier(supplierId);
        cmsTopicUpdateLogEntity.setUpdateAt(DateUtils.now());
        Long topicUpdateLogId = cmsTopicUpdateLogService.updateOrInsertOfNon(cmsTopicUpdateLogEntity);
        param.put("topicUpdateLogId", topicUpdateLogId.toString());
        //保存文件成功,开始解析
        EmaiImapSaveFileEvent emaiImapSaveFileEvent = new EmaiImapSaveFileEvent(param);
        applicationEventPublisher.publishEvent(emaiImapSaveFileEvent);
    }

    /**
     * 文本解码
     *
     * @param encodeText 解码MimeUtility.encodeText(String text)方法编码后的文本
     * @return 解码后的文本
     * @throws UnsupportedEncodingException
     */
    private String decodeText(String encodeText) throws UnsupportedEncodingException {
        if (encodeText == null || "".equals(encodeText)) {
            return "";
        } else {
            return MimeUtility.decodeText(encodeText);
        }
    }


}
